/*
   Copyright (c) 2014 Red Hat, Inc. <http://www.redhat.com>
   This file is part of GlusterFS.

   This file is licensed to you under your choice of the GNU Lesser
   General Public License, version 3 or any later version (LGPLv3 or
   later), or the GNU General Public License, version 2 (GPLv2), in all
   cases as published by the Free Software Foundation.
*/

#ifndef __BARRIER_H__
#define __BARRIER_H__

#include "barrier-mem-types.h"
#include "xlator.h"
#include "timer.h"
#include "call-stub.h"

#define BARRIER_SAFE_ASSIGN(lock, to, value)                    \
        do {                                                    \
                LOCK (&(lock));                                 \
                {                                               \
                        to = value;                             \
                }                                               \
                UNLOCK (&(lock));                               \
        } while (0)

#define BARRIER_FOP_CBK(fop_name, label, frame, this, params ...)       \
        do {                                                            \
                barrier_priv_t         *_priv           = NULL;         \
                call_stub_t            *_stub           = NULL;         \
                gf_boolean_t            _barrier_enabled= _gf_false;    \
                struct list_head        queue           = {0, };        \
                                                                        \
                INIT_LIST_HEAD (&queue);                                \
                                                                        \
                _priv = this->private;                                  \
                GF_ASSERT (_priv);                                      \
                                                                        \
                LOCK (&_priv->lock);                                    \
                {                                                       \
                        if (_priv->barrier_enabled) {                   \
                                _barrier_enabled = _priv->barrier_enabled;\
                                                                        \
                                _stub = fop_##fop_name##_cbk_stub       \
                                        (frame,                         \
                                         default_##fop_name##_cbk_resume,\
                                         params);                       \
                                if (!_stub) {                           \
                                        __barrier_disable (this, &queue);\
                                        goto unlock;                    \
                                }                                       \
                                                                        \
                                __barrier_enqueue (this, _stub);        \
                        }                                               \
                }                                                       \
unlock:                                                                 \
                UNLOCK (&_priv->lock);                                  \
                                                                        \
                if (_stub)                                              \
                        goto label;                                     \
                                                                        \
                if (_barrier_enabled && !_stub) {                       \
                        gf_log (this->name, GF_LOG_CRITICAL,            \
                                "Failed to barrier FOPs, disabling "    \
                                "barrier. FOP: %s, ERROR: %s",          \
                                #fop_name, strerror (ENOMEM));          \
                        barrier_dequeue_all (this, &queue);             \
                }                                                       \
                                                                        \
                STACK_UNWIND_STRICT (fop_name, frame, params);          \
                goto label;                                             \
        } while (0)

typedef struct {
        gf_timer_t       *timer;
        gf_boolean_t      barrier_enabled;
        gf_lock_t         lock;
        struct list_head  queue;
        struct timespec   timeout;
        uint32_t          queue_size;
} barrier_priv_t;

int __barrier_enable (xlator_t *this, barrier_priv_t *priv);
void __barrier_enqueue (xlator_t *this, call_stub_t *stub);
void __barrier_disable (xlator_t *this, struct list_head *queue);
void barrier_timeout (void *data);
void barrier_dequeue_all (xlator_t *this, struct list_head *queue);
call_stub_t *__barrier_dequeue (xlator_t *this, struct list_head *queue);

#endif
